package info.niwota.web.apache;

import info.niwota.commons.acl.AccessControl;
import info.niwota.commons.web.PageColumns;
import info.niwota.commons.web.PageCursor;
import info.niwota.web.ApplicationContext;
import info.niwota.web.Debug;
import info.niwota.web.NVPair;
import info.niwota.web.Session;
import info.niwota.web.Utils;
import info.niwota.web.app.AppHelper;
import info.niwota.web.app.MissingApps;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.List;

import org.json.JSONObject;

import x.org.apache.http.Header;
import x.org.apache.http.HttpRequest;
import x.org.apache.http.HttpResponse;
import x.org.apache.http.protocol.HttpContext;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

/**
 * @author qiangli
 * 
 */
public class ContentHandler extends BaseHandler {
	private static final String TAG = "ContentHandler";
	private SharedPreferences perms;
	//private static final int ACCESS_DEFAULT = AccessControl.ACCESS_LEVEL_DEFAULT;

	public ContentHandler(Context ctx) {
		super(ctx);
		perms = AppHelper.getPerms(ctx);
	}

	private int getPerm(String pkg) {
		return perms.getInt(pkg, AccessControl.ACCESS_LEVEL_DEFAULT);
	}

	/**
	 * Format name-value for sending over content provider
	 * 
	 * @param name
	 * @param value
	 * @return
	 */
	private String nvp(String name, String value, String prefix) {
		if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) {
			return "";
		}
		return prefix + (name + "=" + value);
	}

	@Override
	public boolean handle(String p, HttpRequest req, HttpResponse res,
			HttpContext context) throws Exception {
		final Session s = getSession(context);
		final String user = (s == null ? null : s.user);
		final int group = (s == null ? AccessControl.ACCESS_PUBLIC : s.group);
		
		if (Debug.DEBUG) {
			Log.d(TAG, "handle path: " + p + " user: " + user + " group: " + group + " session: " + s);
		}
		
		if (p.startsWith("/content/")) {
			final Uri uri = Uri.parse("content://" + p.substring(9));
			final String pkg = uri.getAuthority();
			final int perm = getPerm(pkg);
			final boolean denied = (group > perm);
			handleContent(req, res, context, uri, pkg, perm, s, group, user, denied);
			return true;
		}
		//
		return false;
	}

	/**
	 * 
	 * @param req
	 * @param res
	 * @param context
	 * @param uri path segments after /content/<pkg>/
	 * @param pkg
	 * @param perm
	 * @param sess
	 * @param group
	 * @param user
	 * @param denied
	 * @return
	 * @throws Exception
	 */
	private boolean handleContent(HttpRequest req, HttpResponse res,
			HttpContext context, Uri uri, String pkg, int perm, Session sess, int group, String user, boolean denied) throws Exception {
		// authorize
		if (Debug.DEBUG) {
			Log.d(TAG, "perm : " + perm + " group: " + group
					+ " access denied: " + denied);
		}
		
		//
		if (pkg.equals("info.niwota.web")) {
			redirectHome(req, res);
			return true;
		}

		//
		if (denied) {
			sendNotFound(req, res, "Access denied.");
			//redirectLogin(req, res);
			return true;
		}

		// redirect to app's web version if any
		if (webappRedirect(req, res, pkg)) {
			return true;
		}

		// prepare and query app's content provider
		uri = rewriteUri(context, uri, sess, pkg, group, user, req);
		//
		Cursor c = null;
		try {
			ContentResolver cr = ctx.getContentResolver();
			c = cr.query(uri, null, null, null, null);
			//
			if (Debug.DEBUG) {
				Log.d(TAG, "handle uri: " + uri + " cursor: " + c);
			}
			//

			if (c == null) {
				if (c == null) {
					// not found
					contentProviderNotFound(req, res, uri, pkg);
				}
				return true;
			}
			if (c != null && c.moveToFirst()) {

				String pcookies = Utils.prefixCookies(pkg, c
						.getString(PageColumns.ColumnIndice.COOKIES));
				if (pcookies != null) {
					HttpUtils.setCookies(res, pcookies);
				}
				setLastModified(res, c
						.getLong(PageColumns.ColumnIndice.LAST_MODIFIED));
				String contentType = c
						.getString(PageColumns.ColumnIndice.CONTENT_TYPE);
				String encoding = c
						.getString(PageColumns.ColumnIndice.CONTENT_ENCODING);
				int contentLength = c
						.getInt(PageColumns.ColumnIndice.CONTENT_LENGTH);

				// session data
				String data = c.getString(PageColumns.ColumnIndice.SESSION);
				if (!TextUtils.isEmpty(data)) {
					sess.data = data;
				}
				if (Debug.DEBUG) {
					Log.d(TAG, (data == null ? "" : "***********")
							+ "session data set: " + data);
				}
				//
				int type = c.getInt(PageColumns.ColumnIndice.TYPE);

				if (Debug.DEBUG) {
					Log.d(TAG, " type: " + type + " content type: "
							+ contentType + " content length: " + contentLength
							+ " encoding: " + encoding);
				}

				if (type == PageCursor.Type.CONTENT) {
					String content = c
							.getString(PageColumns.ColumnIndice.CONTENT);
					sendContent(req, res, context, content, contentType, encoding);
					return true;
				} else if (type == PageCursor.Type.URI) {
					// use original uri if empty from cursor
					String u = c.getString(PageColumns.ColumnIndice.URI);
					Uri contentUri = (TextUtils.isEmpty(u) ? uri : Uri.parse(u));
					InputStream is = cr.openInputStream(contentUri);
					sendContent(req, res, is, contentType, contentLength);
					return true;
				} else if (type == PageCursor.Type.REDIRECT) {
					String u = c.getString(PageColumns.ColumnIndice.URI);
					sendRedirect(res, u);
					return true;
				} else if (type == PageCursor.Type.ERROR) {
					String content = c
							.getString(PageColumns.ColumnIndice.CONTENT);
					
					Exception e = new Exception(content);
					sendError(req, res, e);
					return true;
				}
			}
		} catch (Exception e) {
			if (Debug.DEBUG) {
				e.printStackTrace();
			}
			sendError(req, res, e);
			return true;
		} finally {
			if (c != null) {
				c.close();
			}
		}
		//
		return true;
	}

	private Uri rewriteUri(HttpContext context, Uri uri, Session sess, String pkg, int group, String user, HttpRequest req) throws UnsupportedEncodingException {
		String q = uri.getQuery();
		String sid = (sess == null ? "" : sess.id);
		q = nvp("sid", sid, (q == null ? "?" : "&"));
		String jid = ApplicationContext.getChannelManager().getJid();
		q += nvp("jid", jid, "&");
		q += nvp("group", group + "", "&");
		q += nvp("user", user, "&");
		q += nvp("data", sess.data, "&");
		
		// form fields
		@SuppressWarnings("unchecked")
		List<NVPair> params = (List<NVPair>) context
				.getAttribute(BaseHandler.PARAMETERS_KEY);
		if (params != null && params.size() > 0) {
			for (NVPair p : params) {
				q += nvp(p.getName(), p.getValue(), "&");
			}
		}
		// pass on cookies

		Header[] cookies = HttpUtils.getCookies(req);
		//
		if (cookies != null) {
			q += "&" + ("cookies=" + HttpUtils.filterCookies(pkg, cookies));
		}
		Uri nuri = Uri.parse(uri.toString() + q);
		
		return nuri;
	}

	private boolean webappRedirect(HttpRequest req, HttpResponse res, String pkg)
			throws Exception {
		if (Debug.DEBUG) {
			Log.d(TAG, "webappRedirect pkg: " + pkg);
		}
		final String[][] webapps = MissingApps.REDIRECTS;
		for (int i = 0; i < webapps.length; i++) {
			if (pkg.equals(webapps[i][0])) {
				if (Debug.DEBUG) {
					Log.d(TAG, "redirecting pkg: " + pkg);
				}
				String uri = webapps[i][1];
				if (uri.startsWith("http://")) {
					sendRedirect(res, uri);
				}

				return true;
			}
		}
		return false;
	}

	private void comingSoon(HttpRequest req, HttpResponse res, Uri uri,
			String pkg) throws Exception {
		if (Debug.DEBUG) {
			Log.d(TAG, "comingSoon: " + uri + " pkg: " + pkg);
		}
		final CharSequence label = findLabel(ctx, pkg);

		JSONObject json = new JSONObject();
		json.put("pkg", pkg);
		json.put("label", label);
		sendAssetData(req, res, "coming_soon.tpl", json);
	}

	private void contentProviderNotFound(HttpRequest req, HttpResponse res,
			Uri uri, String pkg) throws Exception {
		// check coming soon,
		//
		final String[] myapps = MissingApps.LIST;
		for (int i = 0; i < myapps.length; i++) {
			if (pkg.equals(myapps[i]) || pkg.startsWith(myapps[i])) {
				comingSoon(req, res, uri, pkg);
				return;
			}
		}

		// oops page
		if (Debug.DEBUG) {
			Log.d(TAG, "contentProviderNotFound: " + uri + " pkg: " + pkg);
		}
		final CharSequence label = findLabel(ctx, pkg);

		JSONObject json = new JSONObject();
		json.put("pkg", pkg);
		json.put("label", label);
		sendAssetData(req, res, "content_error.tpl", json);
	}
}
